home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-04-20 | 27.4 KB | 861 lines | [TEXT/ttxt] |
- ; Low Level MIDI routines with time-stamping
- ; Written by Kirk Austin 5/17/87
- ; This code is in the public domain and is absolutely free
- ; Note: Be sure and turn off range checking in LS Pascal
- ; to prevent a crash.
-
- ; Serial Chip equates
- SCCRd EQU $1D8
- SCCWr EQU $1DC
- aData EQU 6
- aCtl EQU 2
- bData EQU 4
- bCtl EQU 0
- TBE EQU 2
-
- ; Interrupt vector equates
- Lvl1DT EQU $192
- Lvl2DT EQU $1B2
- RxIntOffsetA EQU 24
- TxIntOffsetA EQU 16
- SpecRecCondA EQU 28
- RxIntOffsetB EQU 8
- TxIntOffsetB EQU 0
- SpecRecCondB EQU 12
-
- ; 6522 equates
- VIA EQU $1D4
- vT1C EQU $800
- vT1CH EQU $A00
- vT1L EQU $C00
- vACR EQU $1600
- vIER EQU $1C00
-
- ; XDEF all routines that need to be accessed externally
-
- XDEF InitSCCA
- XDEF InitSCCB
- XDEF TxMIDIA
- XDEF TxMIDIB
- XDEF RxMIDIA
- XDEF RxMIDIB
- XDEF ResetSCCA
- XDEF ResetSCCB
- XDEF InitTimer
- XDEF LoadTimer
- XDEF StartCounter
- XDEF GetCounter
- XDEF QuitTimer
-
- ; These are the routines for the Modem Port
-
- ; PROCEDURE InitSCCA;
- ; Call this routine at the beginning of your application if
- ; using the modem port for MIDI information transfers.
-
- InitSCCA
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A2,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCRd,A1 ; Get base Read address
- ADD #aCtl,A1 ; Add offset for control
- MOVE.B (A1),D0 ; Dummy read
- MOVE.L (SP),(SP) ; Delay
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #aCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%10000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR InitSCCChan ; branch to common init routine
-
- ; set up interrupt vectors
-
- MOVE.L #Lvl2DT,A0 ; get dispatch table ptr
- MOVE #RxIntOffsetA,D0 ; get offset to Rx vector
- LEA PRxIntHandA,A1 ; point to previous vector storage
- MOVE.L 0(A0,D0),(A1) ; save previous interrupt vector
- LEA RxIntHandA,A1 ; set Rx vector
- MOVE.L A1,0(A0,D0)
- MOVE #TxIntOffsetA,D0 ; get offset to Tx vector
- LEA PTxIntHandA,A1 ; point to previous vector storage
- MOVE.L 0(A0,D0),A1 ; save previous interrupt vector
- LEA TxIntHandA,A1 ; set Tx vector
- MOVE.L A1,0(A0,D0)
- MOVE #SpecRecCondA,D0 ; offset to Special vector
- LEA StubA,A1
- MOVE.L A1,0(A0,D0)
-
- ; initialize the flags & pointers
-
- LEA RxByteInA,A2 ; get the address
- CLR (A2)
- LEA RxByteOutA,A2 ; get the address
- CLR (A2)
- LEA RxQEmptyA,A2 ; get the address
- MOVE #$FFFF,(A2)
- LEA TxByteInA,A2 ; get the address
- CLR (A2)
- LEA TxQEmptyA,A2 ; get the address
- MOVE #$FFFF,(A2)
-
- MOVEM.L (SP)+,D0/A0-A2 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- RTS ; and return
-
- ; This is the common initialization routine for both channels
-
- InitSCCChan
- MOVE.B #4,(A0) ; pointer for SCC reg 4
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%10000100,(A0) ; 32x clock, 1 stop bit
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #1,(A0) ; pointer for SCC reg 1
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; No W/Req
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #3,(A0) ; pointer for SCC reg 3
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; Turn off Rx
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #5,(A0) ; pointer for SCC reg 5
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; Turn off Tx
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #11,(A0) ; pointer for SCC reg 11
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00101000,(A0) ; Make TRxC clock source
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #14,(A0) ; pointer for SCC reg 14
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; Disable BRGen
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #3,(A0) ; pointer for SCC reg 3
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%11000001,(A0) ; Enable Rx
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #5,(A0) ; pointer for SCC reg 5
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%01101010,(A0) ; Enable Tx and drivers
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #15,(A0) ; pointer for SCC reg 15
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001000,(A0) ; Enable DCD int for mouse
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS again
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #1,(A0) ; pointer for SCC reg 1
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010011,(A0) ; Enable interrupts
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001010,(A0) ; Set master int enable
- MOVE.L (SP),(SP) ; Delay
- RTS
-
- ; PROCEDURE TxMIDIA (TheData : integer);
- ; This is the routine to transmit a MIDI byte of data
- ; through the Modem Port. To use this routine place
- ; the byte to be transmitted as the lower 8 bits
- ; of a word on the stack, then call TxMIDIA.
-
- TxMIDIA
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- LEA TxQEmptyA,A3 ; get the address
- TST.B (A3) ; is TxQueue empty?
- BNE TxQEA ; if so branch
- LEA TxByteInA,A3 ; get the address
- MOVE (A3),D0 ; if not add byte to queue
- LEA TxQueueA,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update TxByteIn
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- BRA TxExitA ; and exit
-
- TxQEA
- MOVE.L SCCRd,A0 ; get SCC Read Address
- MOVE.L SCCWr,A1 ; get SCC Write Address
- MOVE #aCtl,D0 ; get index for Ctl
- BTST.B #TBE,0(A0,D0) ; transmit buffer empty?
- BNE FirstByteA ; if so branch
- LEA TxByteInA,A3 ; get the address
- MOVE (A3),D0 ; if not add to queue
- LEA TxQueueA,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update pointer
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- LEA TxQEmptyA,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- BRA TxExitA ; and exit
-
- FirstByteA
- MOVE #aData,D0 ; get index to data
- MOVE.L (SP),(SP) ; Delay
- MOVE.B 9(A6),0(A1,D0) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
-
- TxExitA
- MOVEM.L (SP)+,D0/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- UNLK A6 ; release frame pointer
- MOVE.L (SP)+,A1 ; save return address
- ADD.L #2,SP ; move past data word
- MOVE.L A1,-(SP) ; put address back on stack
- RTS ; and return
-
- ; Function RxMIDIA : LongInt;
- ; This routine gets a byte through the modem port.
- ; To use this routine treat it like a Pascal
- ; function. Leave space on the stack for a longword
- ; of data before calling this routine. If the data
- ; on the stack after
- ; the routine executes is 0 there was no MIDI data available.
- ; If it's non-0 the upper 3 bytes contain the counter
- ; value, the MIDI byte is the low byte.
-
- RxMIDIA
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; save interrupts
- MOVEM.L D0-D1/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; disable interrupts
-
- LEA RxQEmptyA,A3 ; get the address
- TST.B (A3) ; any data available?
- BEQ @1 ; if so, branch
- MOVE.L #0,8(A6) ; if not, return with 0
- BRA RxExitA
- @1 LEA RxByteOutA,A3 ; get the address
- MOVE (A3),D0 ; get index to byte out
- LEA RxQueueA,A2 ; point to queue
- MOVE.L #0,D1 ; clear data register
- MOVE.L 0(A2,D0),D1 ; get MIDI data
- MOVE.L D1,8(A6) ; place on stack for return
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @2
- MOVE #0,D0
- @2 LEA RxByteOutA,A3 ; get the address
- MOVE D0,(A3)
- LEA RxByteInA,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is queue empty?
- BNE RxExitA ; if not exit
- LEA RxQEmptyA,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty, set flag
-
- RxExitA
- MOVEM.L (SP)+,D0-D1/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; restore interrupts
- UNLK A6
- RTS ; and return
-
- ; This is the interrupt routine for receiving through
- ; the modem port. It places the counter value and the
- ; MIDI byte in a circular queue to be
- ; accessed later by the application.
- ; When the system gets this far, A0 contains the
- ; SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address
- ; for this channel. The data addresses are offset by 4
- ; from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may
- ; be used freely.
-
- RxIntHandA
- ORI #$0300,SR ; disable interrupts
-
- @3 MOVE #4,D0 ; get data offset
- CLR.L D1 ; prepare for data
- MOVE.L (SP),(SP) ; Delay
- MOVE.B 0(A0,D0),D1 ; read data from SCC
- MOVE.L (SP),(SP) ; Delay
- LEA RxQueueA,A2 ; point to queue
- LEA RxByteInA,A3 ; get the address
- MOVE (A3),D0 ; get offset to next cell
- LEA Counter,A3 ; get the address
- MOVE.L (A3),D2 ; put counter value in D2
- LSL.L #8,D2 ; shift counter one byte
- ADD.L D2,D1 ; combine counter and data
- MOVE.L D1,0(A2,D0) ; put longword in queue
- LEA RxQEmptyA,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @1
- MOVE #0,D0
- @1 LEA RxByteInA,A3 ; get the address
- MOVE D0,(A3)
-
- @2 BTST.B #0,(A0) ; is there more data?
- BNE @3 ; do it again if there is
-
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
- ; This is the interrupt routine for transmitting a byte
- ; through the modem port. It checks to see if there
- ; is any data to send, and if there is it sends it to
- ; the SCC. If there isn't it resets the TBE interrupt
- ; in the SCC and exits.
- ; When the system gets this far, A0 contains the SCC
- ; base read Ctl address and A1 contains the SCC base
- ; write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control
- ; addresses. D0-D3/A0-A3 are already preserved, so
- ; they may be used freely.
-
- TxIntHandA
- ORI #$0300,SR ; disable interrupts
-
- LEA TxQEmptyA,A3 ; get the address
- TST.B (A3) ; Is queue empty?
- BEQ @1 ; if not branch
- MOVE.B #$28,(A1) ; if so, reset TBE interrupt
- MOVE.L (SP),(SP) ; Delay
- BRA TxIExitA ; and exit
- @1 LEA TxByteOutA,A3 ; get the address
- MOVE (A3),D0 ; get index to next data byte
- LEA TxQueueA,A2 ; point to queue
- MOVE #4,D1 ; get data offset
- MOVE.B 0(A2,D0),0(A1,D1) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
- ADDQ #1,D0 ; update index
- CMP #$100,D0
- BNE @2
- MOVE #0,D0
- @2 LEA TxByteOutA,A3 ; get the address
- MOVE D0,(A3)
- LEA TxByteInA,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is TxQueue empty?
- BNE TxIExitA ; if not exit
- LEA TxQEmptyA,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty set flag
-
- TxIExitA
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
- ; PROCEDURE ResetSCCA;
- ; If you called InitSCCA at the beginning of your
- ; application this
- ; routine must be called when the application
- ; quits or the system will
- ; crash due to the interrupt handling pointers
- ; becoming invalid.
-
- ResetSCCA
- MOVEM.L D0/A0-A1,-(SP) ; save registers
- MOVE SR,-(SP) ; Save interrupts
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #aCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%10000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR ResetSCCChan ; branch to common reset routine
- MOVE.L #Lvl2DT,A0 ; get dispatch table pointer
- MOVE #RxIntOffsetA,D0 ; get offset to Rx vector
- LEA PRxIntHandA,A1 ; point to previous vector storage
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
- MOVE #TxIntOffsetA,D0 ; get offset to Tx vector
- LEA PTxIntHandA,A1 ; set Rx vector
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
-
- MOVE (SP)+,SR ; Restore interrupts
- MOVEM.L (SP)+,D0/A0-A1 ; restore registers
- RTS ; and return
-
- ; This is the common reset routine for both channels
-
- ResetSCCChan
- MOVE.B #15,(A0) ; pointer for SCC reg 15
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001000,(A0) ; Enable DCD int
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS again
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #1,(A0) ; pointer for SCC reg 1
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000001,(A0) ; Enable mouse interrupts
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001010,(A0) ; Set master int enable
- MOVE.L (SP),(SP) ; Delay
- RTS
-
- TxQueueA DCB.B $100,0 ; this is the queue
- TxQEmptyA DC 0 ; the queue empty flag
- TxByteInA DC 0 ; index to next cell in
- TxByteOutA DC 0 ; index to next cell out
- RxQueueA DCB.B $400,0 ; this is the queue
- RxQEmptyA DC 0 ; the empty queue flag
- RxByteInA DC 0 ; index to next cell in
- RxByteOutA DC 0 ; index to next cell out
- PRxIntHandA DC 0 ; Previous interrupt vector
- PTxIntHandA DC 0 ; Previous interrupt vector
-
- ; These are the routines for the Printer Port
-
- ; PROCEDURE InitSCCB;
- ; Call this routine at the beginning of your application if
- ; using the printer port for MIDI information transfers.
-
- InitSCCB
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A2,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCRd,A1 ; Get base Read address
- ADD #bCtl,A1 ; Add offset for control
- MOVE.B (A1),D0 ; Dummy read
- MOVE.L (SP),(SP) ; Delay
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #bCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%01000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR InitSCCChan ; branch to common init routine
-
- ; set up interrupt vectors
-
- MOVE.L #Lvl2DT,A0 ; get dispatch table ptr
- MOVE #RxIntOffsetB,D0 ; get offset to Rx vector
- LEA PRxIntHandB,A1 ; point to previous vector storage
- MOVE.L 0(A0,D0),(A1) ; save previous interrupt vector
- LEA RxIntHandB,A1 ; set Rx vector
- MOVE.L A1,0(A0,D0)
- MOVE #TxIntOffsetB,D0 ; get offset to Tx vector
- LEA PTxIntHandB,A1 ; point to previous vector storage
- MOVE.L 0(A0,D0),A1 ; save previous interrupt vector
- LEA TxIntHandB,A1 ; set Tx vector
- MOVE.L A1,0(A0,D0)
- MOVE #SpecRecCondB,D0 ; offset to Special vector
- LEA StubB,A1
- MOVE.L A1,0(A0,D0)
-
- ; initialize the flags & pointers
-
- LEA RxByteInB,A2 ; get the address
- CLR (A2)
- LEA RxByteOutB,A2 ; get the address
- CLR (A2)
- LEA RxQEmptyB,A2 ; get the address
- MOVE #$FFFF,(A2)
- LEA TxByteInB,A2 ; get the address
- CLR (A2)
- LEA TxQEmptyB,A2 ; get the address
- MOVE #$FFFF,(A2)
-
- MOVEM.L (SP)+,D0/A0-A2 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- RTS ; and return
-
- ; PROCEDURE TxMIDIB (TheData : integer);
- ; This is the routine to transmit a MIDI byte of data
- ; through the Printer Port. To use this routine place
- ; the byte to be transmitted as the lower 8 bits
- ; of a word on the stack, then call TxMIDIB.
-
- TxMIDIB
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- LEA TxQEmptyB,A3 ; get the address
- TST.B (A3) ; is TxQueue empty?
- BNE TxQEB ; if so branch
- LEA TxByteInB,A3 ; get the address
- MOVE (A3),D0 ; if not add byte to queue
- LEA TxQueueB,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update TxByteIn
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- BRA TxExitB ; and exit
-
- TxQEB
- MOVE.L SCCRd,A0 ; get SCC Read Address
- MOVE.L SCCWr,A1 ; get SCC Write Address
- MOVE #bCtl,D0 ; get index for Ctl
- BTST.B #TBE,0(A0,D0) ; transmit buffer empty?
- BNE FirstByteB ; if so branch
- LEA TxByteInB,A3 ; get the address
- MOVE (A3),D0 ; if not add to queue
- LEA TxQueueB,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update pointer
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- LEA TxQEmptyB,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- BRA TxExitB ; and exit
-
- FirstByteB
- MOVE #bData,D0 ; get index to data
- MOVE.L (SP),(SP) ; Delay
- MOVE.B 9(A6),0(A1,D0) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
-
- TxExitB
- MOVEM.L (SP)+,D0/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- UNLK A6 ; release frame pointer
- MOVE.L (SP)+,A1 ; save return address
- ADD.L #2,SP ; move past data word
- MOVE.L A1,-(SP) ; put address back on stack
- RTS ; and return
-
- ; Function RxMIDIB : LongInt;
- ; This routine gets a byte through the printer port.
- ; To use this routine treat it like a Pascal
- ; function. Leave space on the stack for a longword
- ; of data before calling this routine. If the data
- ; on the stack after
- ; the routine executes is 0 there was no MIDI data available.
- ; If it's non-0 the upper 3 bytes contain the counter
- ; value, the MIDI byte is the low byte.
-
- RxMIDIB
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; save interrupts
- MOVEM.L D0-D1/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; disable interrupts
-
- LEA RxQEmptyB,A3 ; get the address
- TST.B (A3) ; any data available?
- BEQ @1 ; if so, branch
- MOVE.L #0,8(A6) ; if not, return with 0
- BRA RxExitB
- @1 LEA RxByteOutB,A3 ; get the address
- MOVE (A3),D0 ; get index to byte out
- LEA RxQueueB,A2 ; point to queue
- MOVE.L #0,D1 ; clear data register
- MOVE.L 0(A2,D0),D1 ; get MIDI data
- MOVE.L D1,8(A6) ; place on stack for return
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @2
- MOVE #0,D0
- @2 LEA RxByteOutB,A3 ; get the address
- MOVE D0,(A3)
- LEA RxByteInB,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is queue empty?
- BNE RxExitB ; if not exit
- LEA RxQEmptyB,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty, set flag
-
- RxExitB
- MOVEM.L (SP)+,D0-D1/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; restore interrupts
- UNLK A6
- RTS ; and return
-
- ; This is the interrupt routine for receiving through
- ; the printer port. It places the counter value and the
- ; MIDI byte in a circular queue to be
- ; accessed later by the application.
- ; When the system gets this far, A0 contains the
- ; SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address
- ; for this channel. The data addresses are offset by 4
- ; from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may
- ; be used freely.
-
- RxIntHandB
- ORI #$0300,SR ; disable interrupts
-
- @3 MOVE #4,D0 ; get data offset
- CLR.L D1 ; prepare for data
- MOVE.L (SP),(SP) ; Delay
- MOVE.B 0(A0,D0),D1 ; read data from SCC
- MOVE.L (SP),(SP) ; Delay
- LEA RxQueueB,A2 ; point to queue
- LEA RxByteInB,A3 ; get the address
- MOVE (A3),D0 ; get offset to next cell
- LEA Counter,A3 ; get the address
- MOVE.L (A3),D2 ; put counter value in D2
- LSL.L #8,D2 ; shift counter one byte
- ADD.L D2,D1 ; combine counter and data
- MOVE.L D1,0(A2,D0) ; put longword in queue
- LEA RxQEmptyB,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @1
- MOVE #0,D0
- @1 LEA RxByteInB,A3 ; get the address
- MOVE D0,(A3)
-
- @2 BTST.B #0,(A0) ; is there more data?
- BNE @3 ; do it again if there is
-
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
- ; This is the interrupt routine for transmitting a byte
- ; through the printer port. It checks to see if there
- ; is any data to send, and if there is it sends it to
- ; the SCC. If there isn't it resets the TBE interrupt
- ; in the SCC and exits.
- ; When the system gets this far, A0 contains the SCC
- ; base read Ctl address and A1 contains the SCC base
- ; write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control
- ; addresses. D0-D3/A0-A3 are already preserved, so
- ; they may be used freely.
-
- TxIntHandB
- ORI #$0300,SR ; disable interrupts
-
- LEA TxQEmptyB,A3 ; get the address
- TST.B (A3) ; Is queue empty?
- BEQ @1 ; if not branch
- MOVE.B #$28,(A1) ; if so, reset TBE interrupt
- MOVE.L (SP),(SP) ; Delay
- BRA TxIExitB ; and exit
- @1 LEA TxByteOutB,A3 ; get the address
- MOVE (A3),D0 ; get index to next data byte
- LEA TxQueueB,A2 ; point to queue
- MOVE #4,D1 ; get data offset
- MOVE.B 0(A2,D0),0(A1,D1) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
- ADDQ #1,D0 ; update index
- CMP #$100,D0
- BNE @2
- MOVE #0,D0
- @2 LEA TxByteOutB,A3 ; get the address
- MOVE D0,(A3)
- LEA TxByteInB,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is TxQueue empty?
- BNE TxIExitB ; if not exit
- LEA TxQEmptyB,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty set flag
-
- TxIExitB
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
- ; PROCEDURE ResetSCCB;
- ; If you called InitSCCB at the beginning of your
- ; application this
- ; routine must be called when the application
- ; quits or the system will
- ; crash due to the interrupt handling pointers
- ; becoming invalid.
-
- ResetSCCB
- MOVEM.L D0/A0-A1,-(SP) ; save registers
- MOVE SR,-(SP) ; Save interrupts
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #bCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%01000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR ResetSCCChan ; branch to common reset routine
- MOVE.L #Lvl2DT,A0 ; get dispatch table pointer
- MOVE #RxIntOffsetB,D0 ; get offset to Rx vector
- LEA PRxIntHandB,A1 ; point to previous vector storage
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
- MOVE #TxIntOffsetB,D0 ; get offset to Tx vector
- LEA PTxIntHandB,A1 ; set Rx vector
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
-
- MOVE (SP)+,SR ; Restore interrupts
- MOVEM.L (SP)+,D0/A0-A1 ; restore registers
- RTS ; and return
-
- TxQueueB DCB.B $100,0 ; this is the queue
- TxQEmptyB DC 0 ; the queue empty flag
- TxByteInB DC 0 ; index to next cell in
- TxByteOutB DC 0 ; index to next cell out
- RxQueueB DCB.B $400,0 ; this is the queue
- RxQEmptyB DC 0 ; the empty queue flag
- RxByteInB DC 0 ; index to next cell in
- RxByteOutB DC 0 ; index to next cell out
- PRxIntHandB DC 0 ; Previous interrupt vector
- PTxIntHandB DC 0 ; Previous interrupt vector
-
- ; This is the space for a special condition interrupt
- ; routine. All I do here is reset the error flag in the SCC
- ; and return. When the system gets this far, A0 contains
- ; the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address
- ; for this channel.
- ; The data addresses are offset by 4 from the control
- ; addresses. D0-D3/A0-A3 are already preserved, so
- ; they may be used freely.
-
- StubA
- ORI #$0300,SR ; Disable interrupts
- MOVE.B #%00110000,(A1) ; Reset Error
- MOVE.L (SP),(SP) ; Delay
- ANDI #$F8FF,SR ; Restore interrupts
- RTS
-
- ; This is the space for a special condition interrupt
- ; routine. All I do here is reset the error flag in the SCC
- ; and return. When the system gets this far, A0 contains
- ; the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address
- ; for this channel.
- ; The data addresses are offset by 4 from the control
- ; addresses. D0-D3/A0-A3 are already preserved, so
- ; they may be used freely.
-
- StubB
- ORI #$0300,SR ; Disable interrupts
- MOVE.B #%00110000,(A1) ; Reset Error
- MOVE.L (SP),(SP) ; Delay
- ANDI #$F8FF,SR ; Restore interrupts
- RTS
-
- ; These are the routines for the counter you can use for
- ; time-stamping the incoming MIDI data. This is useful
- ; for writing sequencer type applications.
- ; The time-stamping is done on an iterrupt level,
- ; is extremely accurate,
- ; and uses the VIA timer #1. This means that you can't
- ; use any of the Sound Manager routines because they use
- ; timer #1 too. If you want to create a metronome click
- ; you have to write your own code that accesses
- ; the sound hardware directly without using timer #1.
- ; InitTimer and LoadTimer expect a word on the stack
- ; to load the timer.
- ; To increment the counter every millisecond, load the
- ; timer with decimal 782. If you aren't going to use
- ; time-stamping you can ignore these routines.
-
- ; PROCEDURE InitTimer ( TimrValue : integer);
- ; Only call InitTimer once at the beginning
- ; of your application 1 millisecond is decimal 782.
-
- InitTimer
- LINK A6,#0 ; set frame pointer
- MOVEM.L D0/A0-A1,-(SP)
- MOVE.L #Lvl1DT,A0 ; Point to level 1 dispatch table
- LEA PrevIVC,A1 ; Point to interrupt vector storage
- MOVE.L 24(A0),(A1) ; save previous interrupt vector
- LEA CounterIntHand,A1 ; point to new interrupt handler
- MOVE.L A1,24(A0) ; put it in the dispatch table
- MOVE.L VIA,A1 ; point to the 6522 chip
- ORI.B #$40,vACR(A1) ; set the timer to freerun mode
- MOVE.B #$C0,vIER(A1) ; Enable timer interrupts
- MOVE 8(A6),D0 ; Get timer value
- MOVE.B D0,vT1L(A1) ; set timer lo byte
- LSR #8, D0 ; shift to hi byte
- MOVE.B D0,vT1CH(A1) ; set timer hi byte
- MOVEM.L (SP)+,D0/A0-A1
- UNLK A6
- MOVE.L (SP)+,A0 ; save return address
- ADDQ #2,SP ; move past timer value
- MOVE.L A0,-(SP) ; replace return address
- RTS
-
- ; PROCEDURE LoadTimer (TimrValue : integer);
- ; Call LoadTimer whenever you want to change the timer value.
- ; 1 millisecond is decimal 782.
-
- LoadTimer
- LINK A6,#0 ; set frame pointer
- MOVEM.L D0/A0-A1,-(SP)
- MOVE.L VIA,A1 ; point to the 6522 chip
- MOVE 8(A6),D0 ; Get timer value
- MOVE.B D0,vT1L(A1) ; set timer lo byte
- LSR #8,D0 ; shift to hi byte
- MOVE.B D0,vT1CH(A1) ; set timer hi byte
- MOVEM.L (SP)+,D0/A0-A1
- UNLK A6
- MOVE.L (SP)+,A0 ; save return address
- ADDQ #2,SP ; move past timer value
- MOVE.L A0,-(SP) ; replace return address
- RTS
-
- ; PROCEDURE StartCounter;
- ; StartCounter sets the counter value to 1
-
- StartCounter
- LEA Counter,A0 ; point to the counter
- MOVE.L #1,(A0) ; set it to 1
- RTS
-
- ; FUNCTION GetCounter : LongInt;
- ; GetCounter returns a longword that is the value
- ; of the counter
-
- GetCounter
- MOVE.L A0,-(SP)
- LEA Counter,A0 ; point to the counter
- MOVE.L (A0),8(SP) ; return it as function result
- MOVE.L (SP)+,A0
- RTS
-
- ; PROCEDURE QuitTimer;
- ; Call QuitTimer when your application is done or the system will crash.
-
- QuitTimer
- MOVEM.L A0-A1,-(SP)
- MOVE.L VIA,A1 ; Disable 6522 interrupts
- MOVE.B #$40,vIER(A1)
- LEA PrevIVC,A1 ; Restore previous interrupt vector
- MOVE.L #Lvl1DT,A0
- MOVE.L (A1),24(A0)
- MOVEM.L (SP)+,A0-A1
- RTS
-
- ; This is the interrupt handler routine for the counter.
- ; When the system gets this far A1 contains the base
- ; address of the VIA.
- ; It also preserves D0-D3/A0-A3.
-
- CounterIntHand
- LEA Counter,A0 ; point to the counter
- ADDQ.L #1,(A0) ; Increment it
- MOVE.B vT1C(A1),D0 ; Clear interrupt flag on 6522
- RTS
-
- Counter DC.L 1 ; The counter
- PrevIVC DC.L 0 ; Previous interrupt vector
-
- END
-
-